/**
 * 
 */
package codec.huffman;

/**
 * @author nquangtrung
 *
 */
public abstract class JPEGHuffmanTable {
	public static final int LUMINANCE_DC 		= 0;
	public static final int LUMINANCE_AC 		= 1;
	public static final int CHROMINANCE_DC 		= 2;
	public static final int CHROMINANCE_AC 		= 3;
	
	/**
	 * Huffman code generation's variance
	 * 
	 * @param hufval Huffman table's HUFVAL array
	 * @param hufsize Huffman table's HUFSIZE array
	 * @param length length of the HUFVAL table (just so this function is different from the other one)
	 * @return generated code
	 */
	public static int[] generateCode(int[] hufval, int[] hufsize, int length) {
		int[] hufcode = new int[length];
		
		hufcode[0] = 0;
		for (int i = 1; i < length; i++) {
			int coef = 1;
			for (int j = 0; j < hufsize[i] - hufsize[i-1]; j++)
				coef *= 2;
			hufcode[i] = (hufcode[i-1]+1) * coef;
		}
		return hufcode;
	}
	/**
	 * Huffman code generation according to itu-t81's proposal
	 * 
	 * @param hufval Huffman table's HUFVAL array
	 * @param hufsize Huffman table's HUFSIZE array
	 * @return generated code
	 */
	public static int[] generateCode(int[] hufval, int[] hufsize) {
		int length = hufval.length;
		
		int[] hufcode = new int[length];
		
		int k = 0;
		int code = 0;
		int si = hufsize[0];
		
		do {
			do {
				hufcode[k] = code;
				code = code + 1;
				k = k + 1;
			} while(k < length && hufsize[k] == si);
			while (k < length && hufsize[k] != si) {
				code = code * 2;
				si = si + 1;
			}
		} while(k < length);
		return hufcode;
	}
	/**
	 * Find the size of <code>diff</code>
	 * @param diff
	 * @return
	 */
	public static int size(int diff) {
		return (diff!=0)?((int)(Math.floor(Math.log(Math.abs(diff)) / Math.log(2))) + 1):0;
	}
	/**
	 * Make EHUFCODE and EHUFSIZE table
	 * 
	 * @param hufval Huffman table's HUFVAL array
	 * @param hufcode Huffman table's HUFCODE array
	 * @param hufsize Huffman table's HUFSIZE array
	 * @param max HUFVAL table's maximum number
	 * 
	 * @return ehuf[0] as EHUFCODE, ehuf[1] as EHUFSIZE 
	 */
	public static int[][] makeEHUF(int[] hufval, int[] hufcode, int[] hufsize, int hufval_max) {
		int[][] ehuf = new int[2][hufval_max + 1];
		for (int i = 0; i < hufval_max; i++) {
			/* 
			 * 0xFFFFFFFF is not permitted in the hufman codec
			 * so if ehufcode[i] = -1 it means that the word 
			 * does not have a code
			 * 
			 */
			ehuf[0][i] = -1;
			ehuf[1][i] = -1;
		}
		for (int i = 0; i < hufval.length; i++) {
			ehuf[0][hufval[i]] = hufcode[i]; // ehufcode
			ehuf[1][hufval[i]] = hufsize[i]; // ehufsize
		}
		
		return ehuf;
	}
	
	public int findCode(int word, int table) {
		int[] ehufcode = this.getEHUFCODE(table);
		return ehufcode[word];
	}
	public int findSize(int word, int table) {
		int[] ehufsize = this.getEHUFSIZE(table);
		return ehufsize[word];
	}
	
	public abstract int[] getBITS(int component);
	public abstract int[] getHUFVAL(int component);
	
	public abstract int[] getEHUFCODE(int table);
	public abstract int[] getEHUFSIZE(int table);
}
